import {
  MODE_LOCAL_NAME,
  PLAYER_LIST_LOCAL_NAME,
  CURRENT_INDEX_LOCAL_NAME,
} from "./constant";
export interface AudioListEvents {
  /**
   * 播放模式变化事件
   */
  modeChage: CustomEvent<PLAYMODETYPE>;
  playerListUpdate: CustomEvent<IplayerList[]>;
  videoListTimeupdate: CustomEvent<{
    progress: string;
    type: string;
    formatDuration: string;
    formatCurrentTime: string;
    duration: number;
    currentTime: number;
  }>;

  switchSongs: CustomEvent<{
    info: IplayerList;
    currentIndex: number;
  }>;
}

type AudioListEventMap = HTMLMediaElementEventMap & AudioListEvents;

const PLAYMODEENUM = {
  /**
   * 循环播放
   */
  LOOP: "loop",
  /**
   * 单曲循环
   */
  SINGLE: "single",
  /**
   * 顺序播放
   */
  ORDER: "order",
  /**
   *  随机播放
   */
  RANDOM: "random",
  /**
   * 无播放模式
   */
  NONE: "none",
} as const;

export type PLAYMODETYPE = (typeof PLAYMODEENUM)[keyof typeof PLAYMODEENUM];

export enum PLAY_AUDIO_TYPE_ENUM {
  /**
   * 主音频
   */
  AUDIO = "audio",
  /**
   * 伴奏音频
   */
  BAN_ZOU = "banZou",
}
/**
 * 播放器列表
 */
export interface IplayerList {
  /**
   * 播放链接
   */
  src: string;
  /**
   * 曲目名称
   */
  title: string;
  /**
   * 序号
   */
  serial: number;

  /**
   * 伴奏音频
   */
  banZouUrl?: string;
  /**
   * 其它扩展字段
   */
  [propsName: string]: any;
}

export interface CreateAudioMethodsParams {
  playerList: IplayerList[];
}

export interface AudioListOptions {
  /**
   * 切换音频时触发事件，返回新的链接，可以在这个事件中做一些事件。
   * @param url
   * @returns
   */
  handleUrl?: (url: string) => Promise<string>;
}

function getNestedValue(obj: any, path: string) {
  return path.split(".").reduce((current, key) => current && current[key], obj);
}
function getRandomInt(min: number, max: number): number {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
 * 格式化秒数为 HH:mm:ss
 * @param duration
 * @returns
 */
function formatDuration(duration: number) {
  const hours = Math.floor(duration / 3600); // 计算小时数
  const minutes = Math.floor((duration - hours * 3600) / 60); // 计算分钟数
  const seconds = Math.floor(duration - hours * 3600 - minutes * 60); // 计算秒数

  // 用两位数表示小时、分钟和秒，不足两位时前面补0
  const hoursStr = hours < 10 ? "0" + hours : hours;
  const minutesStr = minutes < 10 ? "0" + minutes : minutes;
  const secondsStr = seconds < 10 ? "0" + seconds : seconds;

  return `${hoursStr}:${minutesStr}:${secondsStr}`;
}

const CreateSymbol = Symbol("create");
class AudioList extends Audio {
  addEventListener<K extends keyof AudioListEventMap>(
    type: K,
    listener: (this: AudioList, ev: AudioListEventMap[K]) => any,
    options?: boolean | AddEventListenerOptions
  ): void {
    super.addEventListener(type, listener as any, options);
  }

  removeEventListener<K extends keyof AudioListEventMap>(
    type: K,
    listener: (this: AudioList, ev: AudioListEventMap[K]) => any,
    options?: boolean | EventListenerOptions
  ): void {
    super.removeEventListener(type, listener as any, options);
  }
  /**
   * 播放模式
   */
  get mode() {
    return (
      (localStorage.getItem(MODE_LOCAL_NAME) as PLAYMODETYPE) ||
      PLAYMODEENUM.SINGLE
    );
  }

  set mode(mode: PLAYMODETYPE) {
    localStorage.setItem(MODE_LOCAL_NAME, mode);
    this.triggerChangeMode();
  }
  static modes: PLAYMODETYPE[] = Object.values(PLAYMODEENUM);
  /**
   * 变化播放模式
   * @param mode
   */
  changeMode(mode?: PLAYMODETYPE) {
    if (mode) {
      this.mode = mode;
    } else {
      const index = AudioList.modes.findIndex((m) => m === this.mode);
      const resIndex = (index + 1) % AudioList.modes.length;
      this.mode = AudioList.modes[resIndex];
    }
  }

  /**
   * 切换音频类型
   * audio 切换主音频
   * banZou 切换伴奏音频
   * @param type
   */
  changeAudioType(type: PLAY_AUDIO_TYPE_ENUM) {
    this.setCurrentIndex({ index: this.currentIndex, audioType: type });
    this.dispatchEvent(new CustomEvent("changeAudioType", { detail: type }));
  }

  triggerChangeMode() {
    this.dispatchEvent(new CustomEvent("modeChage", { detail: this.mode }));
  }

  /**
   * 当前播放的索引
   */
  private _currentIndex: number = 0;

  /**
   * 关闭，请使用方法 setCurrentIndex
   */
  // set currentIndex(index: number) {
  //   const isPlaying = !this.paused;
  //   const currentSrc = this.playerList[index].src;
  //   if (decodeURI(this.src) === currentSrc) {
  //     return;
  //   }

  //   this.src = this.playerList[index].src;
  //   this._currentIndex = index;
  //   localStorage.setItem(CURRENT_INDEX_LOCAL_NAME, index + "");
  //   if (isPlaying) {
  //     this.play();
  //   }
  //   this.trigreeSwitchSongs();
  // }
  private originSrc?: string;

  /**
   * 在切换音频之前要做的事情。可以重写这个函数，可以根据回调中的参数去获取音乐详情。或做一些其他的事情。
   * @param payload IplayerList
   * @returns
   */
  static async handleChangeIndexAftre(
    payload: IplayerList
  ): Promise<IplayerList> {
    return payload;
  }
  async setCurrentIndex(
    {
      index,
      audioType = PLAY_AUDIO_TYPE_ENUM.AUDIO,
    }: { audioType?: PLAY_AUDIO_TYPE_ENUM; index: number } = {
      index: this.currentIndex,
    }
  ) {
    let payload: IplayerList | undefined;
    if (
      index === this.currentIndex &&
      audioType === PLAY_AUDIO_TYPE_ENUM.BAN_ZOU &&
      (this.currentPlayerInfo?.src || this.currentPlayerInfo?.banZouUrl)
    ) {
      payload = this.currentPlayerInfo;
    } else {
      payload = await AudioList.handleChangeIndexAftre(this.playerList[index]);
    }

    // console.log("payload:", payload);

    let currentSrc = payload.src;
    let currentTime = this.currentTime || 0;
    switch (audioType) {
      case PLAY_AUDIO_TYPE_ENUM.BAN_ZOU:
        currentSrc = payload.banZouUrl || currentSrc;
        break;
    }
    // console.log(
    //   `originSrc:${this.originSrc},currentSrc:${currentSrc},==  ${
    //     this.originSrc === currentSrc
    //   }`
    // );

    if (this.originSrc === currentSrc) {
      return;
    }

    const isPlaying = !this.paused;
    let src = currentSrc;
    try {
      if (this.handleUrl) {
        src = await this.handleUrl(src);
      }
      this.src = src;
    } catch (error) {
      this.src = src;
    }
    const oldPlayerInfo = { ...this.currentPlayerInfo };
    this.playerList = this.playerList.map((item, i) => {
      return index == i ? payload : item;
    });
    // 记录当前播放的原生地址
    this.originSrc = currentSrc;
    this._currentIndex = index;
    localStorage.setItem(CURRENT_INDEX_LOCAL_NAME, index + "");
    if (isPlaying) {
      await this.play();
    }

    if (oldPlayerInfo.id === this.currentPlayerInfo.id) {
      this.currentTime = currentTime;
    }

    this.trigreeSwitchSongs();
  }

  get currentIndex() {
    let index = localStorage.getItem(CURRENT_INDEX_LOCAL_NAME);
    if (index) {
      return parseInt(index);
    } else {
      return this._currentIndex;
    }
  }

  get currentPlayerInfo() {
    return this.playerList[this.currentIndex];
  }

  private constructor(creator?: Symbol) {
    if (creator !== CreateSymbol) {
      throw new Error("请使用 create 静态方法创建实例！");
    }
    super();
  }
  static audioList: AudioList;
  /**
   * 创建播放器
   * @returns
   */
  public static create(options?: AudioListOptions) {
    if (!AudioList.audioList) {
      const audio = new AudioList(CreateSymbol);
      AudioList.audioList = audio;
      audio.init(options);
    }
    return AudioList.audioList;
  }
  private _playerList: CreateAudioMethodsParams["playerList"] = [];
  /**
   * 播放器列表
   */
  get playerList() {
    const localjson = localStorage.getItem(PLAYER_LIST_LOCAL_NAME);
    if (localjson) {
      return JSON.parse(localjson) as CreateAudioMethodsParams["playerList"];
    }
    return this._playerList;
  }

  set playerList(val) {
    this._playerList = val;
    localStorage.setItem(PLAYER_LIST_LOCAL_NAME, JSON.stringify(val));
  }
  /**
   * 设置播放列表
   * @param playerList
   * @param index 设置要播放的索引 默认是 0
   */
  async setPlayerList(
    playerList: CreateAudioMethodsParams["playerList"],
    index: number = 0
  ) {
    this.playerList = playerList;

    await this.setCurrentIndex({ index });
    this.trigreePlayerListUpdate();
  }
  /**
   * 获取当前播放列表
   * @returns
   */
  getPlayerList() {
    return this.playerList;
  }
  /**
   * 上一首播放
   */
  async prev(isPlay?: boolean) {
    const nextIndex = this.currentIndex - 1;
    if (nextIndex < 0) {
      const len = this.playerList.length;
      await this.setCurrentIndex({ index: len - 1 });
    } else {
      await this.setCurrentIndex({ index: nextIndex });
    }

    if (isPlay) {
      await this.play();
    }

    return this.currentPlayerInfo;
  }
  /**
   * 下一首
   */
  async next(isPlay?: boolean) {
    const len = this.playerList.length;
    const nextIndex = this.currentIndex + 1;
    if (nextIndex === len) {
      await this.setCurrentIndex({ index: 0 });
    } else {
      await this.setCurrentIndex({ index: nextIndex });
    }

    if (isPlay) {
      await this.play();
    }

    return this.currentPlayerInfo;
  }
  async randomPlay(isPlay?: boolean) {
    const len = this.playerList.length;
    const nextIndex = getRandomInt(0, len - 1);

    await this.setCurrentIndex({ index: nextIndex });
    if (isPlay) {
      await this.play();
    }

    return this.currentPlayerInfo;
  }

  private handleUrl?: AudioListOptions["handleUrl"];

  init({ handleUrl }: AudioListOptions = {}) {
    this.handleUrl = handleUrl;
    this.addEventListener("ended", this.onEnded.bind(this));
    this.addEventListener("timeupdate", this.onCustomTimeupdate.bind(this));

    this.addEventListener("error", this.onError.bind(this));
    setTimeout(() => {
      if (this.playerList.length > 0) {
        this.setPlayerList(this.playerList, this.currentIndex);
      }
      this.triggerChangeMode();
    }, 1000);
  }
  /**
   * 播放完成事件
   * @param event
   */
  private onEnded() {
    switch (this.mode) {
      case PLAYMODEENUM.LOOP:
        this.next(true);
        break;
      case PLAYMODEENUM.SINGLE:
        this.play();
        break;
      case PLAYMODEENUM.ORDER:
        if (this.playerList.length - 1 === this.currentIndex) return;
        this.next(true);
        break;
      case PLAYMODEENUM.RANDOM:
        this.randomPlay(true);
        break;
      case PLAYMODEENUM.NONE:
        break;
      default:
        const a: never = this.mode;
        break;
    }
  }

  trigreeSwitchSongs() {
    this.dispatchEvent(
      new CustomEvent("switchSongs", {
        detail: {
          info: this.currentPlayerInfo,
          currentIndex: this.currentIndex,
        },
      })
    );
  }
  private onCustomTimeupdate() {
    const event = new CustomEvent("videoListTimeupdate", {
      detail: {
        progress: this.progress,
        type: "videoListTimeupdate",
        formatDuration: formatDuration(this.duration || 0),
        formatCurrentTime: formatDuration(this.currentTime || 0),
        duration: this.duration,
        currentTime: this.currentIndex,
      },
    });
    this.dispatchEvent(event);
  }

  /**
   * 播放进度百分比
   */
  get progress() {
    return this.duration
      ? Math.floor((this.currentTime / this.duration) * 100)
      : 0;
  }
  set progress(pro: number) {
    const durcation = this.duration;
    if (durcation) {
      this.currentTime = (pro / 100) * durcation;
    }
  }
  /**
   * 将不符合 IplayerList 接口规则的对象，转换成符合接口规则的对象。
   * @param item 原始对象
   * @param transformMap 要映身的字段
   * @returns
   * @example
   * AudioList.transformPlayerList(item, {
                  title: "mulu",
                  src: "dmtUrl.adUrl",
                  serial: "xuhao",
                })
   * 
   * 
   */
  static transformPlayerList(item: any, transformMap: IplayerList) {
    let obj: any = {};
    for (let key in transformMap) {
      obj[key] = getNestedValue(item, transformMap[key]);
    }
    return Object.assign(item, obj);
  }

  onError() {
    console.log("播放失败", document.hidden);
    if (document.hidden) {
      this.onEnded();
    }
  }

  trigreePlayerListUpdate() {
    const event = new CustomEvent("playerListUpdate", {
      detail: this.playerList,
    });
    this.dispatchEvent(event);
  }
}

export default AudioList;
